#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <iostream>

#include "GameBoard.h"
#include "Tiles.h"


GameBoard::GameBoard(int width, int height, int canvasWidth, int canvasHeight):
m_height(height),
m_width(width),
m_tileHeight(canvasHeight / height),
m_tileWidth(canvasWidth / width),
m_tiles(),
m_initialized(false)
{
}

GameBoard::~GameBoard()
{
	freeResources();
}

/*
* Generate a random board, the only static things in the board
* are the starting and ending point.  The starting point will Always
* be the bottom left (last row, first column) and the end point
* will always be the top right (first row, last column)
*
* Returns true if the board was successfully created, false otherwise
*/
bool GameBoard::GenerateBoard()
{
	// if we've already generated a board in this object, don't do it again
	if (m_initialized)
	{
		return false;
	}

	int random = 0;
	/* initialize random seed: */
	srand ( time(NULL) );

	for (int row = 0; row < m_height; row ++)
	{
		std::vector<Tile *> colVec;
		for (int col = 0; col < m_width; col++)
		{
			// TODO: ensure these are solvable
			random = rand() % NUM_GENERATABLE_TILES;
			switch (random)
			{
			case 0:
				colVec.push_back(new FreeTile());
				break;
			case 1:
				colVec.push_back(new DirtTile());
				break;
			case 2:
				colVec.push_back(new RockTile());
				break;
			default:
				colVec.push_back(new FreeTile());
			}
		}
		m_tiles.push_back(colVec);
	}

	m_initialized = true;

	return true;
}

void GameBoard::Render()
{
	std::vector<std::vector<Tile *>>::iterator it;
	int row = 0;
	for (it = m_tiles.begin(); it != m_tiles.end(); it++, row++)
	{
		std::vector<Tile *>::iterator rowIt;
		int col = 0;
		for (rowIt = (*it).begin(); rowIt != (*it).end(); rowIt++, col++)
		{
			(*rowIt)->Render(col * m_tileWidth, row * m_tileHeight, m_tileWidth, m_tileHeight);
		}
	}
}

void GameBoard::freeResources()
{
	// delete the dynamically created memory for this game board
	std::vector<std::vector<Tile *>>::iterator it;
	for (it = m_tiles.begin(); it < m_tiles.end(); it++)
	{
		std::vector<Tile *>::iterator rowIt;
		for (rowIt = (*it).begin(); rowIt != (*it).end(); rowIt++)
		{
			delete (*rowIt);
		}
	}

	m_tiles.clear();
}

void GameBoard::GetMoveInfo(Entity * ent, MoveDirection dir, MoveInfo &info)
{
	info.SetEntity(ent);
	info.SetMoveDirection(dir);

	populateMoveInfo(info);
}

void GameBoard::populateMoveInfo(MoveInfo &info)
{
	using namespace std;
	if (NULL == info.GetEntity())
	{
		info.SetIsValid(false);
		return;
	}

	// translate the x,y of the ent to the tile row/col
	Position rowCol;
	TranslateLocationToRowCol(info.GetEntity()->GetPosition(), rowCol);

	if (rowCol.y < m_width && 
		rowCol.x < m_height &&
		rowCol.y >= 0 &&
		rowCol.x >= 0)
	{
		info.SetIsValid(true);

		// modify the row col according to the move requested
		getDestinationRowCol(info.GetDirection(), rowCol);

		// save the rowCol in the MoveInfo, so we don't have to calculate 
		// it again later.
		info.SetDesitinationRowCol(rowCol);

		if (rowCol.y < m_width && 
			rowCol.x < m_height &&
			rowCol.y >= 0 &&
			rowCol.x >= 0)
		{
			// get the destination x,y
			Position dest;
			TranslateRowColToLocation(rowCol, dest);
			info.SetDestination(dest);

			Tile *t = GetTile(rowCol);
			if (NULL == t)
			{
				info.SetIsValid(false);
				return;
			}

			info.SetCost(0);
			if (t->IsTravellable())
			{
				info.SetCost(t->TravelCost());
			}
			else
			{
				info.SetIsValid(false);
				return;
			}
		}
		else
		{
			info.SetIsValid(false);
		}
	}
	else
	{
		info.SetIsValid(false);
	}

	return;
}

void GameBoard::TranslateLocationToRowCol(const Position &pos, Position &outRowCol)
{
	outRowCol.y = pos.x / m_tileWidth;
	outRowCol.x = pos.y / m_tileHeight;
}

void GameBoard::TranslateRowColToLocation(const Position &RowCol, Position &pos)
{
	pos.x = (RowCol.y * m_tileWidth) + (m_tileWidth / 2);
	pos.y = (RowCol.x * m_tileHeight) + (m_tileHeight / 2);
}

/*
*	Returns the tile at the row col passed.  NULL if invalid input
*/
Tile* GameBoard::GetTile(const Position &rowCol)
{
	if (rowCol.x >= 0 &&
		rowCol.x < m_height &&
		rowCol.y >= 0 &&
		rowCol.y < m_width)
	{
		return (m_tiles[rowCol.x])[rowCol.y];
	}

	return NULL;	
}

void GameBoard::getDestinationRowCol(const MoveDirection &dir, Position &rowCol)
{
	switch (dir)
	{
	case MOVE_RIGHT:
		rowCol.y++;
		break;
	case MOVE_LEFT:
		rowCol.y--;
		break;
	case MOVE_UP:
		rowCol.x++;
		break;
	case MOVE_DOWN:
		rowCol.x--;
		break;
	}
}

bool GameBoard::ConsumeTileForMove(const MoveInfo &m)
{
	Position rowCol = m.GetDestinationRowCol();
	Tile *t = GetTile(rowCol);
	if (NULL == t)
	{
		return false;
	}
	
	Tile *replacement = t->Consume();
	if (NULL == replacement)
	{
		return false;
	}
	
	// delete the current tile
	delete (m_tiles[rowCol.x])[rowCol.y];
	(m_tiles[rowCol.x])[rowCol.y] = replacement;
	return true;
}